'use strict';
const _ = require('lodash');

module.exports = app => {
  class App {
    //构造方法
    constructor(modelName) {
      this.model(modelName)
    }

    // 实体命名转换 _转驼峰，首字母小写
    firstUpperCase(str) {
      const re = /_(\w)/g;
      str = str.replace(re, function ($0, $1) {
        return $1.toUpperCase();
      });
      return str[0].toUpperCase() + str.slice(1);
    }

    // 获取模型
    model(modelName) {
      if (typeof modelName !== 'string') {
        throw new Error('实体名称必须为字符串！');
      }
      modelName = this.firstUpperCase(modelName);
      const model = app.model[modelName];
      if (model === undefined) {
        throw new Error('未找到实体对象！');
      }
      this.model = model;
      this.cols = Object.keys(this.model.attributes);
    }

    //拾取参数上与实体相关的属性
    pick(param) {
      if (!param) return undefined
      let obj = {}
      let newArr = this.cols.concat('$and', '$or')
      for (let prop of newArr) {
        if (param.hasOwnProperty(prop)) {
          obj[prop] = param[prop]
        }
      }
      return obj;
    }

    // 插入
    async insert(param) {
      const data = _.pick(param, this.cols);
      const ret = await this.model.build(data).save();
      return ret.get({ plain: true });
    }

    // 根据主键删除
    async delete(param) {
      const pkName = this.model.primaryKeyField;
      const where = {};
      where[pkName] = param[pkName];
      if (where[pkName] == null) {
        throw new Error('条件不正确！')
      }
      const ret = await this.model.destroy({
        where,
      });
      return ret;
    }

    //自定义删除
    async deleteBy(param) {
      const where = _.pick(param, this.cols);
      var keys = Object.keys(where)
      if (keys.length === 0) {
        throw new Error('未解析到任何查询条件，请检查参数！')
      }
      const ret = await this.model.destroy({
        where,
      });
      return ret;
    }

    // 修改
    async update(param) {
      const data = _.pick(param, this.cols);

      const pkName = this.model.primaryKeyField;

      // 获取主键条件
      const where = {};
      where[pkName] = data[pkName];
      // 删除主键数据是因为，它默认生成的update都会更新data里的值来拼接，所以会多个更新主键这种没意义的部分
      delete data[pkName];
      const ret = await this.model.update(data, {
        where,
      });
      return ret;
    }

    // 插入或者修改 使用自带的方法，但是生成的sql语句会生成update主键字段
    async upsert(param) {
      const data = _.pick(param, this.cols);

      const buildData = this.model.build(data);
      const ret = await this.model.upsert(buildData.dataValues);
      return buildData.get({ plain: true });
    }

    // 自己实现的插入或者修改
    async save(param) {
      const pkName = this.model.primaryKeyField;
      const isNewRecord = param[pkName] == null
      if (isNewRecord) {
        delete param[pkName]
      }
      const buildData = this.model.build(param, {
        isNewRecord,
      });
      await buildData.save()
      return buildData.get({ plain: true });
    }



    //查询单个元素
    async findOne(param) {
      let { where, attributes } = param || {}
      where = _.pick(where, this.cols);

      const ret = await this.model.findOne({
        attributes,
        where,
        raw: true,
      });
      return ret
    }

    //查询一组元素
    async findAll(param) {
      let { where, attributes, order } = param || {}
      where = _.pick(where, this.cols);

      const ret = await this.model.findAll({
        where,
        attributes,
        order,
        raw: true,
      });
      return ret
    }

    //分页
    async page(param) {
      let { where, attributes, order, offset, limit, page } = param || {}

      //参数类型处理
      if (limit && typeof limit === 'string') {
        limit = Number(limit)
      }
      if (page && typeof page === 'string') {
        page = Number(page)
      }
      if (!limit) {
        limit = 10
      }
      if (!page) {
        page = 1
      }
      //计算offset
      offset = (page - 1) * limit
      //
      if (!order) {
        order = [
          ['created_at', 'desc']
        ]
      }

      const ret = await this.model.findAndCountAll({
        attributes,
        where: this.pick(where),
        order,
        offset,
        limit,
        raw: true,
      });

      return ret;
    }

    //根据Id查询元素
    async findById(id) {
      const ret = await this.model.findById(id, {
        raw: true
      });
      return ret
    }

    //查询或创建
    async findOrCreate(param) {
      let { where, defaults } = param
      const ret = await this.model.findOrCreate({
        where: this.pick(where),
        defaults: this.pick(defaults),
        raw: true,
      })
      return ret
    }

  }

  return App;
};
